package org.offsync.util

class DisposableBaseState {
  private var disposeState = DisposeState.Live
  
  final def value = disposeState
  
  final def markDisposing(lock: Object): Boolean = {
    if (disposeState == DisposeState.Live) {
      disposeState = DisposeState.Disposing
      lock.notify()
      return true
    } else {
      return false
    }
  }
  
  final def markDisposed(lock: Object): Unit = {
    disposeState = DisposeState.Disposed
    lock.notify()
  }

  final def requireLive(): Unit = {
    if (disposeState != DisposeState.Live) {
      throw new DisposedException(this.getClass + " object is not live")
    }
  }
}

trait DisposableBase extends Disposable with HasLock {
  protected val disposableBaseState = new DisposableBaseState
  
  override def disposeState = lock.synchronized(disposableBaseState.value)
  
  protected def onDispose(): Unit = lock.synchronized(disposableBaseState.markDisposed(lock))
  
  override def waitDisposed(
      deadline: Time): Unit = {
    while (!deadline.expired) {
      lock.synchronized {
        if (disposableBaseState.value == DisposeState.Disposed) {
          return
        }
        
        val timeoutNanoseconds = deadline.timeLeft.nanoseconds max 1
        val timeoutMilliseconds = timeoutNanoseconds / 1000000
        val timeoutNanoPart = (timeoutNanoseconds % 1000000).asInstanceOf[Int]
        lock.wait(timeoutMilliseconds, timeoutNanoPart)
      }
    }
  }
  
  final def isLive: Boolean = lock.synchronized {
    return disposableBaseState.value == DisposeState.Live
  }
  
  final def dispose(): Unit = {
    if (lock.synchronized(disposableBaseState.markDisposing(lock))) {
      onDispose()
    }
  }
}
